#include "si7006.h"
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/device.h>

#define CNAME "si7006"
struct i2c_client* gclient;
int major;
const int count = 1;
struct class* cls;
struct device* dev;

int i2c_read_serial_firware(unsigned short reg)
{
    int ret;
    unsigned char val;
    char r_buf[] = { reg >> 8, reg & 0xff };
    // 1.封装消息结构体
    struct i2c_msg r_msg[] = {
        [0] = {
            .addr = gclient->addr,
            .flags = 0,
            .len = 2,
            .buf = r_buf,
        },
        [1] = {
            .addr = gclient->addr,
            .flags = 1,
            .len = 1,
            .buf = &val,
        },
    };
    // 2.发送消息
    ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));
    if (ret != ARRAY_SIZE(r_msg)) {
        printk("i2c read error\n");
        return -EAGAIN;
    }

    return val;
}
int i2c_read_tmp_hum(unsigned int reg)
{
    char r_buf[] = {reg};
    unsigned short val;
    int ret;
    struct i2c_msg r_msg[] = {
        [0] = {
            .addr = gclient->addr,
            .flags = 0,
            .len = 1,
            .buf = r_buf,
        },            
        [1] = {
            .addr = gclient->addr,
            .flags = 1,
            .len = 2,
            .buf = (__u8*)&val,
        },
    };
    ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));
    if(ret != ARRAY_SIZE(r_msg)){
        printk("i2c读取失败\n");
        return -EAGAIN;
    }
    return (val >> 8 | val << 8);
}


int si7006_open(struct inode* inode, struct file* file)
{
    return 0;
}
long si7006_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    int data, ret;
    switch (cmd) {
    case GET_SERIAL:
        data = i2c_read_serial_firware(SERIAL_ADDR);
        if (data < 0) {
            printk("i2c read serial error\n");
            return data;
        }
        ret = copy_to_user((void*)arg, &data, sizeof(int));
        if (ret) {
            printk("copy data serial to user error\n");
            return -EIO;
        }
        break;
    case GET_FIRWARE:
        data = i2c_read_serial_firware(FIRWARE_ADDR);
        if (data < 0) {
            printk("i2c read firware error\n");
            return data;
        }
        ret = copy_to_user((void*)arg, &data, sizeof(int));
        if (ret) {
            printk("copy data firware to user error\n");
            return -EIO;
        }
        break;
    case GET_TMP:
        data = i2c_read_tmp_hum(TMP_ADDR);
        if (data < 0) {
            printk("i2c read tmp error\n");
            return data;
        }
        data &= 0xffff;
        ret = copy_to_user((void*)arg, &data, sizeof(int));
        if (ret) {
            printk("copy data tmp to user error\n");
            return -EIO;
        }
        break;
    case GET_HUM:
        data = i2c_read_tmp_hum(HUM_ADDR);
        if (data < 0) {
            printk("i2c read hum error\n");
            return data;
        }
        data &= 0xffff;
        ret = copy_to_user((void*)arg, &data, sizeof(int));
        if (ret) {
            printk("copy data hum to user error\n");
            return -EIO;
        }
        break;
    }
    return 0;
}
int si7006_close(struct inode* inode, struct file* file)
{
    return 0;
}
static struct file_operations fops = {
    .open = si7006_open,
    .unlocked_ioctl = si7006_ioctl,
    .release = si7006_close,
};

int si7006_probe(struct i2c_client* client,
    const struct i2c_device_id* id)
{
    gclient = client;
    printk("%s:%d\n", __func__, __LINE__);

    // 1.注册字符设备驱动
    major = register_chrdev(0, CNAME, &fops);
    if (major < 0) {
        printk("register chrdev error\n");
        return -EAGAIN;
    }
    // 2.创建设备节点
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls)) {
        printk("class create error\n");
        return PTR_ERR(cls);
    }
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);
    if (IS_ERR(dev)) {
        printk("device create error\n");
        return PTR_ERR(dev);
    }

    return 0;
}
int si7006_remove(struct i2c_client* client)
{
    printk("%s:%d\n", __func__, __LINE__);
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, CNAME);
    return 0;
}

struct of_device_id oftable[] = {
    {
        .compatible = "hqyj,si7006",
    },
    {}
};
MODULE_DEVICE_TABLE(of, oftable);

struct i2c_driver si7006 = {
    .probe = si7006_probe,
    .remove = si7006_remove,
    .driver = {
        .name = "si7006123456",
        .of_match_table = oftable,
    }
};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");